/**
 * @overview 基于Cesium3D封装的三维地图工具类
 * @author chenli
 * @update 2021-01-07
 */
class CesiumMap3DControl {
  constructor(viewer) {
    this.viewer = viewer // Cesium3D实例化的视图，一切操作均基于该视图
    this.entities = new Map() // 此类维护的地图实体集合
    this.primitives = new Map() // 此类维护的地图基元集合
    this.cesiumHeatmap = null
    this.entityCollection = []
    this.BEIDOUSatellites = new Map()
    this._pinBuilder = new Cesium.PinBuilder()
    this._leftClickHandler = null
    this._postRenderEvent = null
  }

  /**
   * @description 添加实体到三维中，并进行维护
   * @param {String} entityName 实体名称, 重名实体会被更新
   * @param {Cesium.Entity} entity 实体, 此处添加的实体必须是 new Entity实例化出的对象，不能为 JSON(Object)格式
   */
  addEntity(entityName, entity) {
    try {
      entityName = String(entityName)
      if (this.entities.has(entityName)) {
        this.removeEntity(entityName)
      }
      this.entities.set(entityName, entity)
      this.viewer.entities.add(entity)
    } catch (e) {
      throw e
    }
  }

  /**
   * @description 获取 entity
   * @param {String} entityName 实体名称
   */
  getEntity(entityName) {
    return this.entities.get(String(entityName))
  }

  /**
   * @description 获取 所有entity
   * @param {String} entityName 实体名称
   */
  getAllEntity() {
    return this.entityCollection
  }

  /**
   * @description 隐藏实体
   * @param {String} entityName 本类中所维护的实体名称
   */
  hiddenEntity(entityName) {
    try {
      entityName = String(entityName)
      if (this.entities.has(entityName)) {
        this.entities.get(entityName).show = false
      }
    } catch (e) {
      console.log('操作实体失败, ', e)
    }
  }

  /**
   *
   * @param {Object} config
   * {
   *      name: String, 一组Billboard的标识符，必填
   *      points: Array 形如: [
   *                              {
   *                                  coordinates: [], 点的坐标位置
   *                                  url: String, billboard的资源url
   *                                  id: String, billboard的标识符,
   *                                  distanceDisplayCondition: Cesium distanceDisplayCondition
   *                                  ...
   *                              }
   *                          ]
   * }
   */
  createBiliboardCollection(config) {
    if (!config.points) {
      return
    }

    const point = this.viewer.scene.primitives.add(new Cesium.BillboardCollection())
    this.primitives.set(config.name, point)

    for (let i = 0; i < config.points.length; i++) {
      point.add({
        position: Cesium.Cartesian3.fromDegrees(
          config.points[i].coordinates[0],
          config.points[i].coordinates[1]
        ),
        image: config.points[i].url || '',
        id: config.points[i].id,
        heightReference: config.points[i].heightReference,
        distanceDisplayCondition: config.points[i].distanceDisplayCondition,
      })
    }
  }

  // 根据名称删除primitiveCollection
  removeBiliboard(name) {
    this.viewer.scene.primitives.remove(this.primitives.get(name))
    this.primitives.delete(name)
  }

  // 根据名称包含删除 primitiveCollection
  removePrimitivesByContainName(name) {
    const collection = []
    for (const v of this.primitives.keys()) {
      if (v.includes(name)) {
        collection.push({
          name: v,
          primitive: this.primitives.get(v),
        })
      }
    }
    for (const i of collection.values()) {
      this.viewer.scene.primitives.remove(i.primitive)
      this.primitives.delete(i.name)
    }
  }

  /**
   * 引入cesiumheatmapjs 绘制的热力图
   * @param {Object} config 绘制配置 形如:
   *                        {
   *                            bounds: 地理坐标数据 wgs48格式 {west: 118.7872266769, south: 32.1794700623, east: 118.8328027725, north: 32.2095966339}
   *                            data: 数据点
   *                                  [{
   *                                      x:1, 经度
   *                                      y: 1, 纬度
   *                                      value:1 权重
   *                                  }],
   *                            radius: 点位半径
   *                        }
   */
  createCesiumHeatmap(config) {
    try {
      if (!config.bounds) {
        throw Error('未设置边界数据')
      }

      if (!Array.isArray(config.data)) {
        throw Error('数据解析错误')
      }

      this.cesiumHeatmap = CesiumHeatmap.create(this.viewer, config.bounds, {
        backgroundColor: 'rgba(0,0,0,0)',
        radius: config.radius || 10,
        maxOpacity: 0.75,
        minOpacity: 0,
        blur: 0.75,
        gradient: {
          '.3': 'green',
          '.50': 'green',
          '.65': 'yellow',
          '.8': 'orange',
          '.95': 'red',
        },
      })
      this.cesiumHeatmap.setWGS84Data(0, 100, config.data)
    } catch (e) {
      console.log('热力图初始化失败, ', e)
    }
  }

  /**
   * @description 删除热力图
   */
  removeHeatMap() {
    if (this.cesiumHeatmap._layer) {
      this.cesiumHeatmap._cesium.entities.remove(this.cesiumHeatmap._layer)
    }
  }

  /**
   * @description 闪烁点
   * @param {Object} config 配置项
   *                  {
   *                      name: string 名称标识
   *                      position: [][] 位置
   *                      size: Number 像素大小, 默认 10
   *                      interval: 时间间隔 s, 默认 2s
   *                      heightReference: 高度限制 30000
   *                      info: 携带信息
   *                  }
   */
  pointWink(config) {
    const pointEntity = this.viewer.entities.add({
      name: config.name,
      info: config.info,
      point: {
        show: true,
        pixelSize: config.size || 10,
        color: new Cesium.CallbackProperty(function color(time, result) {
          const txf = Math.floor(time.secondsOfDay)
          var result = ''
          if (txf % (config.interval || 2) == 0) {
            //间隔两秒变换
            result = Cesium.Color.RED
          } else {
            // result = Cesium.Color.WHITE;
          }
          return result
        }, false),
        // heightReference: 30000,
      },
      position: Cesium.Cartesian3.fromDegrees(
        Number(config.position[0]),
        Number(config.position[1])
      ), //经纬度
      // label: { //可以在点得旁边显示字
      //     text: '',
      //     scale: 0.8,
      //     fillColor: Cesium.Color.YELLOW,
      //     heightReference: 30000,
      //     verticalOrigin: Cesium.VerticalOrigin.LEFT,
      //     horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
      //     pixelOffset: new Cesium.Cartesian2(10, 20)
      // },
      show: true,
    })
    this.entities.set(config.name, pointEntity)
  }

  /**
   * @description 禁止相机视角进入地下
   * @deprecated 由于webgis修改过地图操作方法，此方法对webgis 地图不使用
   */
  limitCameraToUnderground() {
    const _this = this
    _this.viewer.scene.screenSpaceCameraController.minimumZoomDistance = 100
    _this.viewer.clock.onTick.addEventListener(function () {
      setMinCamera()
    })
    const setMinCamera = function () {
      if (_this.viewer.camera.pitch > 100) {
        _this.viewer.scene.screenSpaceCameraController.enableTilt = false
      }
    }
    let startMousePosition
    let mousePosition
    const handler = new Cesium.ScreenSpaceEventHandler(_this.viewer.canvas)
    handler.setInputAction(function (movement) {
      mousePosition = startMousePosition = Cesium.Cartesian3.clone(movement.position)
      handler.setInputAction(function (movement) {
        const y = mousePosition.y - startMousePosition.y
        if (y > 0) {
          _this.viewer.scene.screenSpaceCameraController.enableTilt = true
        }
      }, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
    }, Cesium.ScreenSpaceEventType.MIDDLE_DOWN)
  }

  /**
   * @description 删除实体
   * @param {String} entityName 本类中所维护的实体名称
   */
  removeEntity(entityName) {
    entityName = String(entityName)
    try {
      if (this.entities.has(entityName)) {
        this.viewer.entities.remove(this.entities.get(entityName))
        this.entities.delete(entityName)
      } else {
        throw new Error(`未找到名称为'${entityName}'的实体`)
      }
    } catch (e) {
      console.log('删除实体失败, ', e)
    }
  }

  /**
   * @description 删除所有实体
   */
  removeAllEntity() {
    try {
      this.entities.forEach((val) => {
        this.viewer.entities.remove(val)
      })
      this.entities = new Map()
    } catch (e) {
      console.log('删除实体失败, ', e)
    }
  }

  /**
   * @description 移除工具类维护的entities
   */
  removeAllEntities() {
    for (const entity of this.entities.values()) {
      this.viewer.entities.remove(entity)
    }
    this.entities.clear()
  }

  /**
   * @description 根据要素名称包含关系移除feature
   * @param {String} containContext 包含的文本
   */
  removeEntityByContainName(containContext) {
    for (const entityName of this.entities.keys()) {
      if (entityName.includes(containContext)) {
        this.viewer.entities.remove(this.entities.get(entityName))
        this.entities.delete(entityName)
      }
    }
  }

  /**
   * @description 根据要素名称包含关系隐藏feature
   * @param {String} containContext 包含的文本
   */
  hiddenEntityByContainName(containContext) {
    for (const entityName of this.entities.keys()) {
      if (entityName.includes(containContext)) {
        this.entities.get(entityName).show = false
      }
    }
  }

  /**
   * @description 根据要素名称包含关系显示feature
   * @param {String} containContext 包含的文本
   */
  showEntityByContainName(containContext) {
    for (const entityName of this.entities.keys()) {
      if (entityName.includes(containContext)) {
        this.entities.get(entityName).show = true
      }
    }
  }

  createEntity(config) {
    try {
      const entity = new Cesium.Entity({
        id: config.id,
        name: config.name,
        position: Cesium.Cartesian3.fromDegrees(...config.position),
        orientation: config.orientation,
        billboard: config.billboard,
        box: config.box,
        cooridor: config.cooridor,
        cylinder: config.cylinder,
        ellipse: config.ellipse,
        ellipsoid: config.ellipsoid,
        label: config.label,
        model: config.model,
        path: config.path,
        plane: config.plane,
        point: config.point,
        polygon: config.polygon,
        polyline: config.polyline,
        polylineVolume: config.polylineVolume,
        rectangle: config.rectangle,
        wall: config.wall,
      })
      this.addEntity(config.name, entity)
    } catch (e) {
      console.log(e)
    }
  }

  /**
   * @description 生成地图引脚标记
   * @param {Object} config
   *                  {
   *                      name: String, 唯一标识引脚的名称，用于维护
   *                      url: String, 图片加载路径或资源
   *                      color: String, rgba(), 引脚颜色
   *                      followSurface: Boolean, 是否贴地，默认 true
   *                      posistion: [], 引脚加载位置
   *                      visibleHeight: [], 可视高度, 距离相机位置的高度多少才显示
   *                  }
   */
  createImagePin(config) {
    try {
      if (!config.name) {
        throw new Error('缺少名称标识')
      }

      if (!config.url) {
        throw new Error('图片资源路径错误')
      }

      config.isFollowSurface = config.isFollowSurface === undefined ? true : config.isFollowSurface
      const _this = this
      // let url = Cesium.buildModuleUrl(config.url);
      let entity = null
      const cameraPin = Cesium.when(
        _this._pinBuilder.fromUrl(
          config.url,
          Cesium.Color.fromCssColorString(config.color || 'rgba(255, 255, 0, 1)'),
          48
        ),
        (canvas) => {
          entity = _this.viewer.entities.add({
            id: config.name,
            position: Cesium.Cartesian3.fromDegrees(config.position[0], config.position[1]),
            billboard: {
              image: canvas.toDataURL(),
              heightReference: config.isFollowSurface
                ? Cesium.HeightReference.CLAMP_TO_GROUND
                : Cesium.HeightReference.NONE,
              verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
              distanceDisplayCondition: config.visibleHeight
                ? new Cesium.DistanceDisplayCondition(
                    config.visibleHeight[0],
                    config.visibleHeight[1]
                  )
                : new Cesium.DistanceDisplayCondition(0, Number.MAX_VALUE),
            },
          })
          entity.info = config.info
          return entity
        }
      )

      Cesium.when(cameraPin, () => {
        this.addEntity(config.name, entity)
      })
    } catch (e) {
      console.log(`图片引脚生成错误, `, e)
    }
  }

  /**
   * @description 生成圆点
   * @param {Object} config 配置项
   *                  {
   *                      name: string 名称标识
   *                      position: [][] 位置
   *                      size: Number 像素大小, 默认 24
   *                      heightReference: 高度限制 30000
   *                      info: 携带信息
   *                      text: stirng 显示文本
   *                  }
   */
  createEntityPoint(config) {
    const pointEntity = this.viewer.entities.add({
      name: config.name,
      info: config.info,
      label: {
        //可以在点得旁边显示字
        text: config.text,
        scale: 1,
        font: '10px',
        fillColor: Cesium.Color.WHITE,
        // heightReference: 30000,
        verticalOrigin: Cesium.VerticalOrigin.LEFT,
        horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
        eyeOffset: new Cesium.Cartesian3(0, 0, -10),
        // pixelOffset: new Cesium.Cartesian2(10, 20)
      },
      point: {
        show: true,
        pixelSize: config.size || 48,
        color: Cesium.Color.fromCssColorString('rgba(0,255,255,1)'),
        // heightReference: 30000,
      },
      position: Cesium.Cartesian3.fromDegrees(
        Number(config.position[0]),
        Number(config.position[1])
      ), //经纬度
      show: true,
    })
    this.entities.set(config.name, pointEntity)
  }

  /**
   * @description 绘制基本三维 polyline 折线
   * @options {Object} config
   *                      {
   *                          coordinates: Array 坐标点数据集合, 形如: [[112,32],[113,32]]
   *                          name: String 名称
   *                          width: Number, 线宽, 默认 3
   *                          clampToGround: Boolean, 贴地属性(贴入地形), 默认 true
   *                          color: String, 线的颜色, 默认 Cesium.Color.YELLOW
   *                      }
   */
  createPolyLine(config) {
    try {
      if (!config.coordinates) {
        throw new Error('折线坐标点格式不正确')
      }

      if (!Cesium.Entity.supportsPolylinesOnTerrain(this.viewer.scene)) {
        throw new Error('该平台不支持 polyline 绘制在地形上')
      }

      // 输入参数格式为 [[],[]], 解析成Cesium支持的格式
      const positions = []
      for (let i = 0; i < config.coordinates.length; i++) {
        positions.push(config.coordinates[i][0])
        positions.push(config.coordinates[i][1])
        positions.push(config.coordinates[i][2] || config.height || 0)
      }

      const polylineEntity = new Cesium.Entity({
        name: config.name,
        show: config.show !== undefined ? config.show : true,
        polyline: new Cesium.PolylineGraphics({
          positions: Cesium.Cartesian3.fromDegreesArrayHeights(positions),
          width: config.width || 3,
          clampToGround: config.clampToGround !== undefined ? config.clampToGround : false,
          material:
            config.material ||
            Cesium.Color.fromCssColorString(config.color || 'rgba(255, 255, 0, 1)'),
          distanceDisplayCondition: config.distanceDisplayCondition,
        }),
      })
      this.addEntity(config.name, polylineEntity)
    } catch (e) {
      console.error('绘制基本三维 polyline 折线失败, ', e)
    }
  }

  /**
   * @description 绘制label
   * @param {*} config
   * @returns { Object|* }
   */
  createLabelEntity(config) {
    if (!Array.isArray(config.position)) {
      return
    }

    try {
      const labelEntity = new Cesium.Entity({
        name: config.name,
        show: config.show === undefined ? true : config.show,
        position: Cesium.Cartesian3.fromDegrees(...config.position),
        info: config.info,

        label: {
          text: config.text || '',
          font: config.font || '24px sans-serif',
          showBackground: config.showBackground !== undefined ? config.showBackground : false,
          backgroundColor: config.backgroundColor || new Cesium.Color(0.165, 0.165, 0.165, 0.8),
          heightReference:
            config.heightReference !== undefined
              ? config.heightReference
              : Cesium.HeightReference.NONE, //开启贴地
          distanceDisplayCondition:
            config.distanceDisplayCondition ||
            new Cesium.DistanceDisplayCondition(0, Number.MAX_VALUE),
          disableDepthTestDistance: Number.POSITIVE_INFINITY,
          fillColor: config.fillColor || Cesium.Color.YELLOW,
          outlineColor: config.outlineColor,
          outlineWidth: config.outlineWidth,
        },
      })
      this.addEntity(config.name, labelEntity)
      return labelEntity
    } catch (e) {
      console.log(`绘制失败, `, e)
    }
  }

  createWallEntity(config) {
    if (!Array.isArray(config.coordinates)) {
      return
    }

    // 处理  coordinates
    let positions = []

    for (const val of config.coordinates.values()) {
      positions = positions.concat(val[0]).concat(val[1])
    }
    positions = Cesium.Cartesian3.fromDegreesArray(positions)
    const maxHeight = new Array(positions.length).fill(config.maxHeight || 2)
    const minHeight = new Array(positions.length).fill(config.minHeight || 0)

    try {
      const wallEntity = new Cesium.Entity({
        name: config.name,
        show: config.show === undefined ? true : config.show,
        wall: {
          positions: positions,
          maximumHeights: maxHeight,
          minimumHeights: minHeight,
          shadows: Cesium.ShadowMode.ENABLED,
          distanceDisplayCondition:
            config.distanceDisplayCondition ||
            new Cesium.DistanceDisplayCondition(0, Number.MAX_VALUE),
          material: config.fillColor || new Cesium.Color(0, 1, 1, 0.8), //Cesium.Color.fromCssColorString(config.fillColor || 'rgba(255, 255,255,0.1)'),
        },
      })
      this.addEntity(config.name, wallEntity)
      return wallEntity
    } catch (e) {
      console.log('绘制失败, ', e)
    }
  }

  /**
   * @description 绘制三维基本椭圆
   * @param {Object} config
   *                  {
   *                      name: String, 名称, 唯一标识
   *                      coordinate: Array[][], 位置信息 必填
   *                      isFollowSurface: Boolean, 是否贴地, 默认true
   *                      semiMajorAxis： Number 半长轴大小 单位m, 默认 200
   *                      semiMinorAxis: Number 半短轴 单位m, 默认 100
   *                      fillColor: Rgba String, 填充颜色, 默认 rgba(255, 255, 255, .1)
   *                      strokeColor: Rgba String, 椭圆颜色 rgba(255, 255, 0, 1)
   *                      strokeWidth: Number, 椭圆宽度, 1
   *                  }
   */
  createEllipseEntity(config) {
    if (!Array.isArray(config.coordinate)) {
      return
    }

    // 设置默认贴地
    config.isFollowSurface = config.isFollowSurface === undefined ? true : config.isFollowSurface

    try {
      const ellipseEntity = new Cesium.Entity({
        name: config.name,
        show: config.show === undefined ? true : config.show,
        position: Cesium.Cartesian3.fromDegrees(
          config.coordinate[0],
          config.coordinate[1],
          config.coordinate[2] || 0
        ),
        ellipse: new Cesium.EllipseGraphics({
          semiMajorAxis: config.semiMajorAxis || 400,
          semiMinorAxis: config.semiMinorAxis || 200,
          material: Cesium.Color.fromCssColorString(config.fillColor || 'rgba(255, 255,255,0.1)'),
          outline: true,
          outlineWidth: config.strokeWidth || 1,
          outlineColor: Cesium.Color.fromCssColorString(
            config.strokeColor || 'rgba(255, 255, 0, 1)'
          ),
          heightReference: config.heightReference || Cesium.HeightReference.NONE,
          height: config.coordinate[2] || 0,
          distanceDisplayCondition: config.distanceDisplayCondition,
        }),
      })
      this.addEntity(config.name, ellipseEntity)
      return ellipseEntity
    } catch (e) {
      console.log(`绘制椭圆失败, `, e)
    }
  }

  /**
   * @description 绘制贴地多边形
   * @param {Object} config
   *                 {
   *                      name: String, 名称, 唯一标识
   *                      show: Boolean, 显隐状态，默认 true
   *                      coordinates: Array[][], 位置信息 必填,
   *                      holes: [], 多边形镂空数据
   *                      fillColor: String, 填充颜色, 默认 rgba(255, 255,255,0.1)
   *                      distanceDisplayCondition
   *                  }
   */
  createPolygonEntity(config) {
    if (!Array.isArray(config.coordinates)) {
      return
    }

    // 处理  coordinates
    let positions = []

    for (const val of config.coordinates.values()) {
      positions = positions
        .concat(val[0])
        .concat(val[1])
        .concat(val[2] || 0)
    }
    positions = Cesium.Cartesian3.fromDegreesArrayHeights(positions)

    // 处理转向
    let orientation = undefined
    if (config.position) {
      const position = Cesium.Cartesian3.fromDegrees(...config.position)
      if (config.orientation) {
        const hpr = new Cesium.HeadingPitchRoll(
          config.orientation.heading,
          config.orientation.pitch,
          config.orientation.roll
        )
        orientation = Cesium.Transforms.headingPitchRollQuaternion(position, hpr)
      }
    }

    // 处理 holes
    const holes = []
    if (Array.isArray(config.holes)) {
      for (const hole of config.holes.values()) {
        let coordinates = []
        for (const coordinate of hole.values()) {
          coordinates = coordinates
            .concat(coordinate[0])
            .concat(coordinate[1])
            .concat(coordinate[2] || 0)
        }
        holes.push({
          positions: Cesium.Cartesian3.fromDegreesArrayHeights(coordinates),
        })
      }
    }

    try {
      const polygonEntity = new Cesium.Entity({
        name: config.name,
        show: config.show === undefined ? true : config.show,
        position: config.position ? Cesium.Cartesian3.fromDegrees(...config.position) : undefined,
        orientation: orientation,
        polygon: new Cesium.PolygonGraphics({
          hierarchy: {
            positions: positions,
            holes: holes,
          },
          height: config.height || 0,
          extrudedHeight: config.extrudedHeight,
          perPositionHeight: config.perPositionHeight,
          heightReference:
            config.heightReference != undefined
              ? config.heightReference
              : Cesium.HeightReference.NONE,
          material:
            config.color ||
            Cesium.Color.fromCssColorString(config.fillColor || 'rgba(255, 255,255,0.1)'),
          distanceDisplayCondition: config.distanceDisplayCondition,
          outline: config.outline,
          outlineColor: config.outlineColor,
          outlineWidth: config.outlineWidth,
        }),
        label: config.label,
        info: config.info,
      })
      // polygonEntity.polygon.disableDepthTestDistance = Number.MAX_SAFE_INTEGER// label不被地形遮挡
      polygonEntity.label.disableDepthTestDistance = Number.POSITIVE_INFINITY // label不被地形遮挡
      this.addEntity(config.name, polygonEntity)
      return polygonEntity
    } catch (e) {
      console.log(`绘制多边形失败, `, e)
    }
  }

  /**
   * @description 加载北斗卫星轨迹czml，数据更新于 2020.01.17
   * @param typeList 项目中提供的北斗卫星名称
   */
  loadSatellite(typeList) {
    if (!Array.isArray(typeList) || typeList <= 0) {
      return
    }

    for (const satelliteName of typeList.values()) {
      if (this.BEIDOUSatellites.has(satelliteName)) {
        this.BEIDOUSatellites.get(satelliteName).show = true
      } else {
        const _this = this
        const satellitePromise = Cesium.CzmlDataSource.load(`satelliteCZML/${satelliteName}.czml`)
        satellitePromise
          .then((satellite) => {
            _this.viewer.dataSources.add(satellite)
            _this.BEIDOUSatellites.set(satelliteName, satellite)
          })
          .otherwise((e) => {
            console.log(`卫星${satelliteName}数据加载失败`, e)
          })
      }
    }
  }

  /**
   * @description 取消加载北斗卫星轨迹数据
   * @param {String} satelliteName 卫星名称
   */
  hiddenSatellite(satelliteName) {
    if (this.BEIDOUSatellites.has(satelliteName)) {
      this.BEIDOUSatellites.get(satelliteName).show = false
    }
  }

  /**
   * @description 隐藏所有卫星数据
   */
  hiddenAllSatellite() {
    for (const satellite of this.BEIDOUSatellites.values()) {
      satellite.show = false
    }
  }

  /**
   * @description 移除北斗卫星轨迹数据
   * @param {String} satelliteName 卫星名称
   */
  removeSatellite(satelliteName) {
    if (this.BEIDOUSatellites.has(satelliteName)) {
      this.viewer.dataSources.remove(this.BEIDOUSatellites.get(satelliteName), true)
    }
  }

  /**
   * @description 移除所有卫星数据
   */
  removeAllSatellite() {
    for (const satellite of this.BEIDOUSatellites.values()) {
      this.dataSources.remove(satellite, true)
    }
    this.BEIDOUSatellites.clear()
  }

  /**
   * @description 添加模型
   * @param {Object} config
   *                  {
   *                      name: String，名称，唯一标识
   *                      show: Boolean, 显隐状态， 默认 true
   *                      position: [], 位置信息， 必填
   *                      rotate: Number, degree, 偏角(待定)
   *                      uri: String, 模型地址
   *                      text: String, 文本信息，请修改方法
   *                  }
   */
  createEntityModel(config) {
    if (!Array.isArray(config.position)) {
      return
    }
    const position = Cesium.Cartesian3.fromDegrees(
      config.position[0],
      config.position[1],
      config.position[2]
    )
    const heading = Cesium.Math.toRadians(config.rotate || 0)
    const pitch = 0
    const roll = 0
    const hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll)
    const orientation = Cesium.Transforms.headingPitchRollQuaternion(position, hpr)

    // 生成entity
    const entity = new Cesium.Entity({
      id: config.name,
      position: position,
      orientation: orientation,
      model: {
        uri: config.uri,
        minimumPixelSize: 32,
        // maximumScale: 100
      },
      label: {
        show: true,
        text: config.text || '',
        font: '12px sans-serif',
        pixelOffset: new Cesium.Cartesian2(0, 20),
        fillColor: Cesium.Color.YELLOW,
      },
    })
    // this.viewer.trackedEntity = entity;
    this.addEntity(config.name, entity)
  }

  /**
   * @description 新建静态模型
   * @param config
   */
  createStaticModel(config) {
    if (!config || !config.model) {
      // console.log('需提供模型参数');
      // return;
    }

    if (!Array.isArray(config.position)) {
      console.log('需提供位置信息参数')
      return
    }

    if (!config.name) {
      console.log('需提供唯一标识名称')
      return
    }

    let orientation = undefined
    const position = Cesium.Cartesian3.fromDegrees(...config.position)
    if (config.orientation) {
      const hpr = new Cesium.HeadingPitchRoll(
        Cesium.Math.toRadians(config.orientation.heading),
        config.orientation.pitch,
        config.orientation.roll
      )
      orientation = Cesium.Transforms.headingPitchRollQuaternion(position, hpr)
    }
    const entity = this.viewer.entities.add({
      show: config.show === undefined ? true : config.show,
      id: config.name,
      position: position,
      // position: config.position,
      orientation: orientation,
      model: config.model,
      label: config.label,
      polygon: config.polygon,
    })
    // entity.show = false;
    this.entities.set(config.name, entity)

    //此处防止内存泄漏
    let flag = false
    if (this.entityCollection.length == 0) {
      this.entityCollection.push(entity)
    }
    this.entityCollection.forEach((ele, index) => {
      if (ele.id == config.name) {
        flag = true
        this.entityCollection[index] == entity
      }
    })
    if (flag == false) {
      this.entityCollection.push(entity)
    }
    return entity
  }

  /**
   * 模拟人物移动
   * @param {*} entityName
   * @param {*} time
   * @param {*} config
   * @returns
   */
  runSimulation(entityName, time, config) {
    if (!config || !config.model) {
      console.log('需提供模型参数')
      return
    }

    if (!Array.isArray(config.model.positions)) {
      console.log('需提供位置信息参数')
      return
    }

    const coordiantes = []
    for (const val of config.model.positions.values()) {
      coordiantes.push(val[0])
      coordiantes.push(val[1])
      coordiantes.push(val[2] || 2)
    }
    const positions = Cesium.Cartesian3.fromDegreesArrayHeights(coordiantes)

    const totalTime = parseInt(time || 0) || 360
    const start = this.viewer.clock.currentTime
    // let stop = Cesium.JulianDate.addSeconds(start, totalTime, new Cesium.JulianDate());
    const aminationStop = Cesium.JulianDate.addSeconds(start, 10000, new Cesium.JulianDate())

    // 计算总距离来进行匀速动画
    let allDis = 0
    for (let index = 0; index < positions.length - 1; index++) {
      const dis = Cesium.Cartesian3.distance(positions[index], positions[index + 1])
      allDis += dis
    }
    const v = allDis / totalTime
    // 转换传入点参数为笛卡尔参数点
    const property = new Cesium.SampledPositionProperty()
    let t = 0
    for (let i = 1; i < positions.length; i++) {
      if (i === 1) {
        property.addSample(start, positions[0])
      }
      const dis = Cesium.Cartesian3.distance(positions[i], positions[i - 1])
      const time = dis / v + t
      const julianDate = Cesium.JulianDate.addSeconds(start, time, new Cesium.JulianDate())
      property.addSample(julianDate, positions[i])
      t += dis / v
    }
    property.addSample(aminationStop, positions[positions.length - 1])

    // // 转换传入点参数为笛卡尔参数点
    // let property = new Cesium.SampledPositionProperty();
    // for (let i = 0; i < config.model.positions.length; i++) {
    //     let time = Cesium.JulianDate.addSeconds(start, i * Math.floor(totalTime / config.model.positions.length), new Cesium.JulianDate());
    //     let position = Cesium.Cartesian3.fromDegrees(config.model.positions[i][0], config.model.positions[i][1]);
    //     property.addSample(time, position);
    // }

    // 确保 viewer 在限定的时间内
    this.viewer.clock.shouldAnimate = true
    this.viewer.clock.startTime = start.clone()
    this.viewer.clock.stopTime = aminationStop.clone()
    this.viewer.clock.currentTime = start.clone()
    this.viewer.clock.clockRange = Cesium.ClockRange.CLAMPED //Loop at the end?
    this.viewer.clock.multiplier = 1
    // this.viewer.timeline.zoomTo(start, stop);

    const route_ps = [positions[0]]
    // 生成entity
    const entity = this.viewer.entities.add({
      id: entityName,
      name: entityName,
      position: property,
      orientation: new Cesium.VelocityOrientationProperty(property),
      availability: new Cesium.TimeIntervalCollection([
        new Cesium.TimeInterval({
          start: start,
          stop: aminationStop,
        }),
      ]),
      model: config.model,
      label: config.label,
      polyline: {
        show: false,
        positions: new Cesium.CallbackProperty(function (time, result) {
          // 多余值不需要再添加到路径中
          if (
            JSON.stringify(route_ps[route_ps.length - 1]) != JSON.stringify(property.getValue(time))
          ) {
            route_ps.push(property.getValue(time))
          }
          return route_ps
        }),
        width: 5,
        material: Cesium.Color.RED,
        // clampToGround: true,
      },
    })
    // entity.show = false;
    this.entities.set(entityName, entity)
  }

  /**
   * @description 动态模拟模型移动
   * @param {String} entityName 实体名称用来维护
   * @param time 一次模拟需要完成的时间 单位 s
   * @param config cesium参数配置
   * {
   *      model: {
   *              positions: Array[][] 路线经纬度坐标
   *              uri: String 模型uri
   *          }
   *      ... 可扩充
   * }
   * @param
   *
   */
  createModelMoveEntity(entityName, time, config) {
    const _this = this
    if (!config || !config.model) {
      console.log('需提供模型参数')
      return
    }

    if (!Array.isArray(config.model.positions)) {
      console.log('需提供位置信息参数')
      return
    }

    try {
      // 挂载CgsOverlay地图弹框
      if (config.overlays) {
        for (const o of config.overlays.values()) {
          _this.map.addOverlay(o.overlay)
          _this.map.getOverlayById(o.id).setVisible(o.show)
        }
      }

      // 计算最后点位置, 用来调整方向
      const last2P = config.model.positions[config.model.positions.length - 2]
      const last1P = config.model.positions[config.model.positions.length - 1]
      const n = 10
      const dx = (last1P[0] - last2P[0]) / n
      const dy = (last1P[1] - last2P[1]) / n
      const last = [last1P[0] + dx, last1P[1] + dy, last1P[2]]

      const coordiantes = []
      for (const val of config.model.positions.values()) {
        coordiantes.push(val[0])
        coordiantes.push(val[1])
        coordiantes.push(val[2] || 0)
      }
      const positions = Cesium.Cartesian3.fromDegreesArrayHeights(coordiantes)

      const totalTime = parseInt(time || 0) || 360
      const start = this.viewer.clock.currentTime
      const stop = Cesium.JulianDate.addSeconds(start, totalTime, new Cesium.JulianDate())
      const aminationStop = Cesium.JulianDate.addSeconds(start, 10000, new Cesium.JulianDate())

      // 计算总距离来进行匀速动画
      let allDis = 0
      for (let index = 0; index < positions.length - 1; index++) {
        const dis = Cesium.Cartesian3.distance(positions[index], positions[index + 1])
        allDis += dis
      }
      const v = allDis / totalTime
      // 转换传入点参数为笛卡尔参数点
      const property = new Cesium.SampledPositionProperty()
      let t = 0
      for (let i = 1; i < positions.length; i++) {
        if (i === 1) {
          property.addSample(start, positions[0])
        }
        const dis = Cesium.Cartesian3.distance(positions[i], positions[i - 1])
        const time = dis / v + t
        const julianDate = Cesium.JulianDate.addSeconds(start, time, new Cesium.JulianDate())
        property.addSample(julianDate, positions[i])
        t += dis / v
      }
      property.addSample(aminationStop, Cesium.Cartesian3.fromDegrees(...last))
      const orientationProperty = new Cesium.VelocityOrientationProperty(property)

      // // 转换传入点参数为笛卡尔参数点
      // let property = new Cesium.SampledPositionProperty();
      // for (let i = 0; i < config.model.positions.length; i++) {
      //     let time = Cesium.JulianDate.addSeconds(start, i * Math.floor(totalTime / config.model.positions.length), new Cesium.JulianDate());
      //     let position = Cesium.Cartesian3.fromDegrees(config.model.positions[i][0], config.model.positions[i][1]);
      //     property.addSample(time, position);
      // }

      // 确保 viewer 在限定的时间内
      this.viewer.clock.shouldAnimate = true
      this.viewer.clock.startTime = start.clone()
      this.viewer.clock.stopTime = aminationStop.clone()
      this.viewer.clock.currentTime = start.clone()
      this.viewer.clock.clockRange = Cesium.ClockRange.CLAMPED //Loop at the end?
      this.viewer.clock.multiplier = 1
      const ellipsoid = this.viewer.scene.globe.ellipsoid

      // 生成entity
      const e = new Cesium.Entity({
        id: entityName,
        position: new Cesium.CallbackProperty((time, result) => {
          const cartesian = property.getValue(time)
          const cartographic = ellipsoid.cartesianToCartographic(cartesian)
          // 将弧度转换为度的十进制表示
          const lng = Cesium.Math.toDegrees(cartographic.longitude)
          const lat = Cesium.Math.toDegrees(cartographic.latitude)
          const height = cartographic.height
          if (config.overlays) {
            for (const o of config.overlays.values()) {
              o.overlay.setPosition([lng, lat, height])
            }
          }
          return property.getValue(time)
        }),
        orientation: orientationProperty,
        model: config.model,
        label: config.label,
        // path: config.path,
        // rectangle: {
        //     show: false,
        //     coordinates: new Cesium.CallbackProperty(function (time) {
        //         return Cesium.EllipseGeometry.computeRectangle({
        //             center: property.getValue(time),
        //             semiMajorAxis: 4, // 长轴
        //             semiMinorAxis: 2, // 短轴
        //             rotation: Cesium.Quaternion.computeAngle(orientationProperty.getValue(time) || Cesium.Quaternion.ZERO)
        //         })
        //     }, false),
        // },
        info: config.info,
        overlays: config.overlays,
      })
      const entity = this.viewer.entities.add(e)
      // entity.show = false;
      this.entities.set(entityName, entity)
    } catch (e) {
      console.log(e)
    }
  }

  /**
   * @description 创建移动Entity
   * @param name 唯一标识
   * @param {*} time 完成时间
   * @param {*} config 配置项
   * @returns
   */
  moveEntity(name, time, config) {
    if (!name) {
      console.log('需要提供唯一标识')
      return
    }

    if (!config || !config.model) {
      console.log('需提供模型参数')
      return
    }

    if (!Array.isArray(config.model.positions)) {
      console.log('需提供位置信息参数')
      return
    }

    const coordiantes = []
    for (const val of config.model.positions.values()) {
      coordiantes.push(val[0])
      coordiantes.push(val[1])
      coordiantes.push(val[2] || 0)
    }
    const positions = Cesium.Cartesian3.fromDegreesArrayHeights(coordiantes)

    const totalTime = parseInt(time || 0) || 360
    const start = this.viewer.clock.currentTime
    const aminationStop = Cesium.JulianDate.addSeconds(start, 10000, new Cesium.JulianDate())

    // 计算总距离来进行匀速动画
    let allDis = 0
    for (let index = 0; index < positions.length - 1; index++) {
      const dis = Cesium.Cartesian3.distance(positions[index], positions[index + 1])
      allDis += dis
    }
    const v = allDis / totalTime
    // 转换传入点参数为笛卡尔参数点
    const property = new Cesium.SampledPositionProperty()
    let t = 0
    for (let i = 1; i < positions.length; i++) {
      if (i === 1) {
        property.addSample(start, positions[0])
      }
      const dis = Cesium.Cartesian3.distance(positions[i], positions[i - 1])
      const time = dis / v + t
      const julianDate = Cesium.JulianDate.addSeconds(start, time, new Cesium.JulianDate())
      property.addSample(julianDate, positions[i])
      t += dis / v
    }
    property.addSample(aminationStop, positions[positions.length - 1])

    // 确保 viewer 在限定的时间内
    this.viewer.clock.shouldAnimate = true
    this.viewer.clock.startTime = start.clone()
    this.viewer.clock.stopTime = aminationStop.clone()
    this.viewer.clock.currentTime = start.clone()
    this.viewer.clock.clockRange = Cesium.ClockRange.CLAMPED //Loop at the end?
    this.viewer.clock.multiplier = 1

    const routeP = [positions[0]]
    // 生成entity
    const entity = {
      position: property,
      orientation: new Cesium.VelocityOrientationProperty(property),
      availability: new Cesium.TimeIntervalCollection([
        new Cesium.TimeInterval({
          start: start,
          stop: aminationStop,
        }),
      ]),
      model: config.model,
      billboard: config.billboard,
      label: config.label,
      polyline: {
        show: config.path !== undefined ? config.path.show : false,
        positions: new Cesium.CallbackProperty(function (time, result) {
          if (routeP[routeP.length - 1] != property.getValue(time)) {
            routeP.push(property.getValue(time))
          }

          return routeP
        }),
        width: config.path !== undefined ? config.path.width : 5,
        material: config.path !== undefined ? config.path.color : Cesium.Color.RED,
        clampToGround: config.path !== undefined ? config.path.clampToGround : false,
      },
    }

    this.addEntity(name, entity)
  }

  // 淹没效果
  stretchEntity(config) {
    if (!config.maxHeight) {
      console.log('水漫高度未设置')
      return
    }

    if (!config.time) {
      console.log('水漫动画未设置')
      return
    }

    if (!config.bounds || !Array.isArray(config.bounds)) {
      console.log('边界未设置')
      return
    }

    // 确保地形挡住Polygon
    this.viewer.scene.globe.depthTestAgainstTerrain = true

    // 确保动画
    this.viewer.clock.shouldAnimate = true
    this.viewer.clock.clockRange = Cesium.ClockRange.CLAMPED //Loop at the end?
    // 时间设置
    const start = this.viewer.clock.currentTime
    this.viewer.clock.multiplier = 1
    // let animationStop = Cesium.JulianDate.addSeconds(start, Number.MAX_SAFE_INTEGER, new Cesium.JulianDate());
    const stretchHeightProperty = new Cesium.SampledProperty(Number)
    // stretchHeightProperty.addSample(animationStop, config.maxHeight);
    stretchHeightProperty.addSample(start, config.minHeight)
    stretchHeightProperty.addSample(
      Cesium.JulianDate.addSeconds(start, config.time, new Cesium.JulianDate()),
      config.maxHeight
    )

    // 转换坐标点
    const coordiantes = []
    for (const val of config.bounds.values()) {
      coordiantes.push(val[0])
      coordiantes.push(val[1])
      coordiantes.push(config.minHeight || 0)
    }
    const positions = Cesium.Cartesian3.fromDegreesArrayHeights(coordiantes)

    // 生成实体
    const stetchEntityOpt = {
      polygon: {
        hierarchy: positions,
        material: config.color || new Cesium.Color(0, 1, 1, 0.6),
        // height: 0,
        perPositionHeight: true,
        extrudedHeight: new Cesium.CallbackProperty(function (time, result) {
          return stretchHeightProperty.getValue(time)
        }),
      },
    }
    const entity = this.viewer.entities.add(stetchEntityOpt)
    this.entities.set(config.id, entity)
  }

  // 流淌火效果
  flowingFire(config) {
    const that = this
    const xArr = []
    const yArr = []

    if (!config.maxHeight) {
      console.log('水漫高度未设置')
      return
    }

    if (!config.time) {
      console.log('流淌火动画未设置')
      return
    }

    if (!config.bounds || !Array.isArray(config.bounds)) {
      console.log('边界未设置')
      return
    }

    // 确保地形挡住Polygon
    this.viewer.scene.globe.depthTestAgainstTerrain = true

    // 确保动画
    this.viewer.clock.shouldAnimate = true
    this.viewer.clock.clockRange = Cesium.ClockRange.CLAMPED

    // 时间设置
    const start = this.viewer.clock.currentTime

    //控制蔓延速率
    this.viewer.clock.multiplier = 0.2
    const animationStop = Cesium.JulianDate.addSeconds(
      start,
      Number.MAX_SAFE_INTEGER,
      new Cesium.JulianDate()
    )
    const stretchHeightProperty = new Cesium.SampledProperty(Number)
    stretchHeightProperty.addSample(animationStop, config.maxHeight)
    stretchHeightProperty.addSample(start, config.minHeight)
    stretchHeightProperty.addSample(
      Cesium.JulianDate.addSeconds(start, config.time, new Cesium.JulianDate()),
      config.maxHeight
    )

    // 转换坐标点
    const coordiantes = []
    const centerPointCoordiantes = []
    for (const val of config.bounds) {
      coordiantes.push(val[0])
      coordiantes.push(val[1])
      coordiantes.push(config.minHeight || 0)
    }
    const initPositions = Cesium.Cartesian3.fromDegreesArrayHeights(coordiantes)

    //灾害中心点
    const x0 = config.centerPoint[0]
    const y0 = config.centerPoint[1]
    const z0 = config.centerPoint[2]
    centerPointCoordiantes.push(x0, y0, z0)

    //坐标转换，wgs84转换成笛卡尔空间坐标系。
    const centerPoint = config.centerPoint
    const spreadList = []
    const spreadParam = config.spreadPointsNum
    const boundsArr = config.bounds
    const pointNum = boundsArr.length
    //中心点arr
    for (let i = 0; i < spreadParam; i++) {
      spreadList.push(JSON.parse(JSON.stringify(config.centerPoint)))
    }
    for (let i = 0; i < boundsArr.length; i++) {
      xArr.push(boundsArr[i][0])
      yArr.push(boundsArr[i][1])
    }

    //  灵活的圆形扩散
    const stetchEntityOpt = {
      id: 'entity',
      description: 'liutanghuo',
      polygon: {
        id: 'entity',
        hierarchy: new Cesium.CallbackProperty(function (time, result) {
          //原生数组
          const coordiantes = []
          spreadList.forEach((ele, k) => {
            //判断是否在多边形内，在的话继续往外扩散。
            const status = that.inPolygonOrNot(
              pointNum,
              xArr,
              yArr,
              spreadList[k][0],
              spreadList[k][1]
            )
            if (status == true && stretchHeightProperty.getValue(time) <= config.maxHeight) {
              spreadList[k][0] =
                centerPoint[0] +
                ((0.02 * (stretchHeightProperty.getValue(time) - config.minHeight)) /
                  (config.maxHeight - config.minHeight)) *
                  Math.sin((k * 2 * Math.PI) / spreadParam)
              spreadList[k][1] =
                centerPoint[1] +
                ((0.018 * (stretchHeightProperty.getValue(time) - config.minHeight)) /
                  (config.maxHeight - config.minHeight)) *
                  Math.cos((k * 2 * Math.PI) / spreadParam)
            }
          })
          if (
            stretchHeightProperty.getValue(time) >=
            config.minHeight + (config.maxHeight - config.minHeight) / 2
          ) {
            return initPositions
          }
          for (const val of spreadList) {
            coordiantes.push(val[0])
            coordiantes.push(val[1])
            coordiantes.push(config.minHeight || 0)
          }
          const positions = Cesium.Cartesian3.fromDegreesArrayHeights(coordiantes)
          return positions
        }),
        material: config.color || new Cesium.Color(1.0, 0.8, 0.15, 0.6),
        perPositionHeight: true,
        extrudedHeight: new Cesium.CallbackProperty(function (time, result) {
          return stretchHeightProperty.getValue(time)
        }),
      },
    }
    const entity = this.viewer.entities.add(stetchEntityOpt)
    this.entities.set(config.id, entity)
  }

  //判断点是否在多边形内，pointNum点数;x数组;y数组;测试点经度;测试点纬度;
  inPolygonOrNot(pointNum, xArr, yArr, testx, testy) {
    let i = 0,
      j = 0,
      c = 0
    for (i = 0, j = pointNum - 1; i < pointNum; j = i++) {
      if (
        yArr[i] >= testy != yArr[j] >= testy &&
        testx <= ((xArr[j] - xArr[i]) * (testy - yArr[i])) / (yArr[j] - yArr[i]) + xArr[i]
      ) {
        c = !c
      }
    }
    return c == 0 ? false : true
  }

  //清除绘制图形的点击事件
  clearALL(handler) {
    handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK)
    handler.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE)
    handler.removeInputAction(Cesium.ScreenSpaceEventType.RIGHT_CLICK)
  }

  //绘制出来的entity的拖拽、旋转
  movePolygonEntity(viewer, handler) {
    const that = this
    let previousCoordinates = ''
    let leftDownFlag = false
    let rightDownFlag = false
    let pointDraged = null
    // LEFT_DOWN
    handler.setInputAction(function (movement) {
      const earthPosition = viewer.scene.pickPosition(movement.position)
      pointDraged = viewer.scene.pick(movement.position) //选取当前的entity
      leftDownFlag = true
      if (pointDraged.hasOwnProperty('id')) {
        that.cameraController(viewer, false)
      }
    }, Cesium.ScreenSpaceEventType.LEFT_DOWN)

    that.clearALL(handler)

    // MOUSE_MOVE
    handler.setInputAction(function (movement) {
      // //记录尾随的坐标
      const startRay = viewer.camera.getPickRay(movement.startPosition)
      const endRay = viewer.camera.getPickRay(movement.endPosition)
      //笛卡尔
      const startPosition = viewer.scene.globe.pick(startRay, viewer.scene)
      const endPosition = viewer.scene.globe.pick(endRay, viewer.scene)
      //WGS84弧度坐标
      const startPositionCartographic = Cesium.Cartographic.fromCartesian(startPosition)
      const endPositionCartographic = Cesium.Cartographic.fromCartesian(endPosition)
      if (leftDownFlag === true && pointDraged != null && pointDraged.hasOwnProperty('id')) {
        //拖拽对象为线或面时
        if (Cesium.defined(pointDraged.id.polyline) || Cesium.defined(pointDraged.id.polygon)) {
          //计算每次的偏差
          const changed_x = endPosition.x - startPosition.x
          const changed_y = endPosition.y - startPosition.y
          const changed_z = endPosition.z - startPosition.z
          const currentsPoint = []
          if (Cesium.defined(pointDraged.id.polyline)) {
            previousCoordinates = pointDraged.id.polyline.positions.getValue()
            for (let i = 0; i < previousCoordinates.length; i++) {
              const obj = {}
              obj.x = previousCoordinates[i].x + changed_x
              obj.y = previousCoordinates[i].y + changed_y
              obj.z = previousCoordinates[i].z + changed_z
              currentsPoint.push(obj)
            }
            pointDraged.id.polyline.positions = new Cesium.CallbackProperty(function () {
              return currentsPoint
            }, false)
            // pointDraged.id.polyline.positions = currentsPoint;
          } else {
            previousCoordinates = pointDraged.id.polygon.hierarchy.getValue()
            for (let i = 0; i < previousCoordinates.length; i++) {
              previousCoordinates[i].x += changed_x
              previousCoordinates[i].y += changed_y
              previousCoordinates[i].z += changed_z
              currentsPoint.push(previousCoordinates[i])
            }
            pointDraged.id.polygon.hierarchy = new Cesium.CallbackProperty(function () {
              return currentsPoint
            }, false)
          }
        }

        //拖拽对象为矩形时
        if (Cesium.defined(pointDraged.id.rectangle)) {
          previousCoordinates = pointDraged.id.rectangle.coordinates.getValue()

          //图形偏移
          const changed_x = endPositionCartographic.longitude - startPositionCartographic.longitude
          const changed_y = endPositionCartographic.latitude - startPositionCartographic.latitude
          const changed_z = endPositionCartographic.height - startPositionCartographic.height
          const currentsPoint = JSON.parse(JSON.stringify(previousCoordinates))

          currentsPoint.west = previousCoordinates.west + changed_x
          currentsPoint.south = previousCoordinates.south + changed_y
          currentsPoint.east = previousCoordinates.east + changed_x
          currentsPoint.north = previousCoordinates.north + changed_y
          currentsPoint.height = previousCoordinates.height + changed_z

          //标注偏移
          const currentsLabel = JSON.parse(JSON.stringify(pointDraged.id.position.getValue()))
          currentsLabel.x += endPosition.x - startPosition.x
          currentsLabel.y += endPosition.y - startPosition.y
          currentsLabel.z += endPosition.z - startPosition.z

          //使用Cesium.CallbackProperty 动态修改参数
          //实体
          pointDraged.id.rectangle.coordinates = new Cesium.CallbackProperty(function () {
            return currentsPoint
          }, false)
          //标注
          pointDraged.id.position = new Cesium.CallbackProperty(function () {
            return currentsLabel
          }, false)
        }

        //拖拽对象为椭圆时
        if (Cesium.defined(pointDraged.id.ellipse)) {
          const position_end = endPosition
          const cartographic_end = Cesium.Cartographic.fromCartesian(position_end)
          const height_end = cartographic_end.height
          pointDraged.id.ellipse.height = new Cesium.CallbackProperty(function () {
            return height_end
          }, false)
        }
      }

      //旋转
      if (rightDownFlag === true && pointDraged != null) {
        const entityCenter = {}
        const movingPoint = {}
        //旋转对象为矩形时
        if (pointDraged.id.rectangle) {
          entityCenter.x =
            (pointDraged.id.rectangle.coordinates.getValue().east +
              pointDraged.id.rectangle.coordinates.getValue().west) /
            2
          entityCenter.y =
            (pointDraged.id.rectangle.coordinates.getValue().south +
              pointDraged.id.rectangle.coordinates.getValue().north) /
            2
          movingPoint.x = endPositionCartographic.longitude
          movingPoint.y = endPositionCartographic.latitude

          //旋转角度
          pointDraged.id.rectangle.rotation = new Cesium.CallbackProperty(function () {
            return that.computeRotation(entityCenter, movingPoint)
          }, false)
        }
      }
    }, Cesium.ScreenSpaceEventType.MOUSE_MOVE)

    //LEFT_UP
    handler.setInputAction(function () {
      leftDownFlag = false
      pointDraged = null
      that.cameraController(viewer, true)
    }, Cesium.ScreenSpaceEventType.LEFT_UP)

    //RIGHT_DOWN
    handler.setInputAction(function (movement) {
      pointDraged = viewer.scene.pick(movement.position) //选取当前的entity
      rightDownFlag = true
      if (pointDraged.hasOwnProperty('id')) {
        that.cameraController(viewer, false)
      }
    }, Cesium.ScreenSpaceEventType.RIGHT_DOWN)

    //RIGHT_UP
    handler.setInputAction(function () {
      rightDownFlag = false
      pointDraged = null
      that.cameraController(viewer, true)
    }, Cesium.ScreenSpaceEventType.RIGHT_UP)
  }

  //根据图形几何中心和鼠标移动点计算旋转角度,坐标值需为WGS84 平面坐标，弧度角度无所谓  例 centerPoint:{x:1.5,y:2.6}
  computeRotation(centerPoint, movingPoint) {
    rotation =
      (Math.acos(
        (movingPoint.y - centerPoint.y) /
          Math.sqrt(
            Math.pow(centerPoint.x - movingPoint.x, 2) + Math.pow(centerPoint.y - movingPoint.y, 2)
          )
      ) *
        180) /
      Math.PI
    //当鼠标移动点移动到第二和第三象限时
    if (movingPoint.x < centerPoint.x) {
      rotation = 2 * Math.PI - rotation
    }
    return rotation
  }

  //控制相机的视角
  cameraController(viewer, boolean) {
    viewer.scene.screenSpaceCameraController.enableInputs = boolean //禁止旋转
    viewer.scene.screenSpaceCameraController.enableRotate = boolean //禁止旋转
    viewer.scene.screenSpaceCameraController.enableTranslate = boolean // 禁止移动
    viewer.scene.screenSpaceCameraController.enableZoom = boolean //禁止缩放
    viewer.scene.screenSpaceCameraController.enableTilt = boolean //禁止倾斜相机
    viewer.scene.screenSpaceCameraController.enableLook = boolean
  }

  //获取屏幕点击处的笛卡尔空间坐标输入参数:屏幕坐标   movement.position
  getCatesian3FromPX(px) {
    this.viewer.scene.globe.depthTestAgainstTerrain = true
    if (this.viewer && px) {
      const picks = this.viewer.scene.drillPick(px)
      let cartesian = null
      let isOn3dtiles = false,
        isOnTerrain = false
      for (const i in picks) {
        const pick = picks[i]
        //判断点击处是否是3DTiles。
        if (
          (pick && pick.primitive instanceof Cesium.Cesium3DTileFeature) ||
          (pick && pick.primitive instanceof Cesium.Cesium3DTileset) ||
          (pick && pick.primitive instanceof Cesium.Model)
        ) {
          //模型上拾取
          isOn3dtiles = true
        }
        // 3dtilset
        if (isOn3dtiles) {
          this.viewer.scene.pick(px) // pick,这个得打开，否则获取不准。
          cartesian = this.viewer.scene.pickPosition(px)
          if (cartesian) {
            const cartographic = Cesium.Cartographic.fromCartesian(cartesian)
            if (cartographic.height < 0) {
              cartographic.height = 0
            }
            const lon = Cesium.Math.toDegrees(cartographic.longitude),
              lat = Cesium.Math.toDegrees(cartographic.latitude),
              height = cartographic.height
            cartesian = this.transformWGS84ToCartesian({
              lng: lon,
              lat: lat,
              alt: height,
            })
          }
        }
      }
      // 地形
      const boolTerrain = this.viewer.terrainProvider instanceof Cesium.EllipsoidTerrainProvider
      // Terrain
      if (!isOn3dtiles && !boolTerrain) {
        const ray = this.viewer.scene.camera.getPickRay(px)
        if (!ray) {
          return null
        }
        cartesian = this.viewer.scene.globe.pick(ray, this.viewer.scene)
        isOnTerrain = true
      }
      // 地球
      if (!isOn3dtiles && !isOnTerrain && boolTerrain) {
        cartesian = this.viewer.scene.camera.pickEllipsoid(px, this.viewer.scene.globe.ellipsoid)
      }
      if (cartesian) {
        const position = this.transformCartesianToWGS84(cartesian)
        if (position.alt < 0) {
          cartesian = this.transformWGS84ToCartesian(position, 0.1)
        }
        return cartesian
      }
      return false
    }
  }

  addEntityToMap() {
    const positionData = [
      Cesium.Cartesian3.fromDegrees(118.81334081111906, 32.266762215042675, 50),
      Cesium.Cartesian3.fromDegrees(118.81943719567111, 32.266920675943936, 50),
      Cesium.Cartesian3.fromDegrees(118.81958889406637, 32.264364201589096, 50),
      Cesium.Cartesian3.fromDegrees(118.81345529495948, 32.264165062779924, 50),
      Cesium.Cartesian3.fromDegrees(118.81332496283822, 32.2644524268742, 50),
    ]

    const polygonEntity = {
      id: `test1`,
      description: 'test1',
      name: `test1`,
      position: Cesium.Cartesian3.fromDegrees(118.81634081111906, 32.265762215042675),
      polygon: {
        hierarchy: positionData,
        material: Cesium.Color.RED.withAlpha(0.3),
        outline: true,
        outlineColor: Cesium.Color.BLACK, //边界线颜色
        outlineWidth: 1.0,
        perPositionHeight: false,
        //放开则不贴地
        // extrudedHeight:50,
        heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
        // distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 220)
      },
      label: {
        text: '现场指挥部',
        font: '20px san-serif',
        heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
        distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 5000), //设置可见高度
      },
    }

    const pointEntity1 = {
      // 添加广告牌实体
      name: '标点',
      position: {
        x: -2601863.5396376015,
        y: 4729998.282332502,
        z: 3385423.8477593367,
      },
      label: {
        //文字标签
        text: '企业',
        font: '500 30px Helvetica',
        scale: 0.5,
        style: Cesium.LabelStyle.FILL,
        fillColor: Cesium.Color.WHITE,
        pixelOffset: new Cesium.Cartesian2(0, -75), //偏移量
        showBackground: true,
        backgroundColor: new Cesium.Color(0.5, 0.6, 1, 1.0),
      },
      billboard: {
        image: require('./oneStatus.png'),
        horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
        verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
        scale: 1.5,
        // heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
      },
    }
    window.map3DControl.viewer.entities.add(pointEntity1)
    //测试点上图
    //请求数据

    const qy = require('/public/json/wxyTest/qy.json')
    const cgq = require('/public/json/wxyTest/cgq.json')
    const wxy = require('/public/json/wxyTest/wxy.json')

    //绘图
    qy.forEach((ele) => {
      const pointEntity = {
        // 添加广告牌实体
        name: '标点',
        position: Cesium.Cartesian3.fromDegrees(ele.longitude, ele.latitude),
        label: {
          //文字标签
          text: '企业',
          font: '500 30px Helvetica',
          scale: 0.5,
          style: Cesium.LabelStyle.FILL,
          fillColor: Cesium.Color.WHITE,
          pixelOffset: new Cesium.Cartesian2(0, -75), //偏移量
          showBackground: true,
          backgroundColor: new Cesium.Color(0.5, 0.6, 1, 1.0),
        },
        billboard: {
          image: require('./oneStatus.png'),
          horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
          verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
          scale: 1.5,
          heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
        },
      }
      window.map3DControl.viewer.entities.add(pointEntity)
    })
    wxy.forEach((ele) => {
      const pointEntity = {
        // 添加广告牌实体
        name: '标点',
        position: Cesium.Cartesian3.fromDegrees(ele.longitude, ele.latitude),
        label: {
          //文字标签
          text: '危险园',
          font: '500 30px Helvetica',
          scale: 0.5,
          style: Cesium.LabelStyle.FILL,
          fillColor: Cesium.Color.WHITE,
          pixelOffset: new Cesium.Cartesian2(0, -75), //偏移量
          showBackground: true,
          backgroundColor: new Cesium.Color(0.5, 0.6, 1, 1.0),
        },
        billboard: {
          image: require('./superstat_link.png'),
          horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
          verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
          scale: 1.5,
          heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
        },
      }
      window.map3DControl.viewer.entities.add(pointEntity)
    })
    cgq.forEach((ele) => {
      const pointEntity = {
        // 添加广告牌实体
        name: '标点',
        position: Cesium.Cartesian3.fromDegrees(ele.longitude, ele.latitude),
        label: {
          //文字标签
          text: '传感器',
          font: '500 30px Helvetica',
          scale: 0.5,
          style: Cesium.LabelStyle.FILL,
          fillColor: Cesium.Color.WHITE,
          pixelOffset: new Cesium.Cartesian2(0, -75), //偏移量
          showBackground: true,
          backgroundColor: new Cesium.Color(0.5, 0.6, 1, 1.0),
        },
        billboard: {
          image: require('./threeStatus.png'),
          horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
          verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
          scale: 1.5,
          heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
        },
      }
      window.map3DControl.viewer.entities.add(pointEntity)
    })

    window.map3DControl.viewer.entities.add(polygonEntity)
    // window.map3DControl.viewer.zoomTo(polygonEntity);
  }
  //添加广告牌实体
  addPointEntity(option) {
    // id  name   Cartesian3  lng   lat   label  imgType
    // 添加广告牌实体
    let imgUrl = ''
    switch (option.imgType) {
      case 'oneStatus':
        imgUrl = require('./oneStatus.png')
        break
      case 'threeStatus':
        imgUrl = require('./threeStatus.png')
        break
      default:
        imgUrl = require('./threeStatus.png')
        break
    }

    const pointEntity = {
      id: option.id || '',
      name: option.name || '',
      position: option.Cartesian3 || Cesium.Cartesian3.fromDegrees(option.lng, option.lat),
      label: {
        //文字标签
        text: option.label || '',
        font: '500 30px Helvetica',
        scale: 0.5,
        style: Cesium.LabelStyle.FILL,
        fillColor: Cesium.Color.WHITE,
        pixelOffset: new Cesium.Cartesian2(0, -75), //偏移量
        showBackground: true,
        backgroundColor: new Cesium.Color(0.5, 0.6, 1, 1.0),
      },
      billboard: {
        image: imgUrl,
        horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
        verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
        scale: 1,
      },
    }
    window.map3DControl.viewer.entities.add(pointEntity)
  }

  //绘制线实体
  addPolylineEntity(option) {
    // id  name   Cartesian3List
    // positions: [{x:Cartesian3,y:Cartesian3,z:Cartesian3},{x,y,z},{x,y,z}]
    const polylineEntity = {
      id: option.id,
      name: option.name,
      polyline: {
        positions: option.Cartesian3List,
        material: Cesium.Color.RED.withAlpha(0.7),
        // depthFailMaterial: new Cesium.ColorMaterialProperty(Cesium.Color.GREEN.withAlpha(0.7)),
        width: 8,
        clampToGround: true,
        distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 30000), //设置可见高度
      },
    }
    window.map3DControl.viewer.entities.add(polylineEntity)
  }

  //绘制面实体
  addPolygonEntity(option) {
    // id  name  labelPosition  Cartesian3List   entityColor  label
    // position :{x:Cartesian3,y:Cartesian3,z:Cartesian3}
    // hierarchy:[{x:Cartesian3,y:Cartesian3,z:Cartesian3},{x,y,z},{x,y,z}]
    const polygonEntity = {
      id: option.id,
      name: option.name,
      position: option.labelPosition,
      polygon: {
        hierarchy: option.Cartesian3List,
        material: option.entityColor,
        outline: true,
        outlineColor: Cesium.Color.BLACK, //边界线颜色
        outlineWidth: 1.0,
        perPositionHeight: false,
        //放开则不贴地
        // extrudedHeight:50,
        heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
        distanceDisplayCondition: new Cesium.DistanceDisplayCondition(10, 30000), //设置可见高度
      },
      label: {
        text: option.label,
        font: 'normal 30px MicroSoft YaHei',
        heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
        distanceDisplayCondition: new Cesium.DistanceDisplayCondition(200, 3000), //设置可见高度
      },
    }
    window.map3DControl.viewer.entities.add(polygonEntity)
  }

  //删除所有实体
  removeEntityById(array) {
    array.forEach((ele) => {
      window.map3DControl.viewer.entities.removeById(ele)
    })
  }

  /***
   * 坐标转换 笛卡尔转84
   *
   * @param {Object} Cartesian3 三维位置坐标
   *
   * @return {Object} {lng,lat,alt} 地理坐标
   */
  transformCartesianToWGS84(cartesian) {
    if (this.viewer && cartesian) {
      const ellipsoid = Cesium.Ellipsoid.WGS84
      const cartographic = ellipsoid.cartesianToCartographic(cartesian)
      return {
        lng: Cesium.Math.toDegrees(cartographic.longitude),
        lat: Cesium.Math.toDegrees(cartographic.latitude),
        alt: cartographic.height,
      }
    }
  }
  /***
   * 坐标转换 84转笛卡尔
   *
   * @param {Object} {lng,lat,alt} 地理坐标
   *
   * @return {Object} Cartesian3 三维位置坐标
   */
  transformWGS84ToCartesian(position, alt) {
    if (this.viewer) {
      return position
        ? Cesium.Cartesian3.fromDegrees(
            position.lng || position.lon,
            position.lat,
            (position.alt = alt || position.alt),
            Cesium.Ellipsoid.WGS84
          )
        : Cesium.Cartesian3.ZERO
    }
  }

  /**
   *
   * @param {Object} config
   *              {
   *                  routeList: [],
   *                  name: String,
   *                  height: Number
   *              }
   */
  drawShineLine(config) {
    if (!config.routeList || !Array.isArray(config.routeList)) {
      console.log('无路径')
      return
    }

    config.routeList.forEach((item, index) => {
      const route = []
      for (const val of item.coordinates.values()) {
        route.push(val[0])
        route.push(val[1])
        route.push(val[2] || item.height || 0)
      }

      const data = {
        flowing: true,
        flowImage: require('../../../assets/images/common/white.png'), //飞行线的图片
        options: {
          name: `${config.name}_${item.name}_${index}`,
          polyline: {
            positions: Cesium.Cartesian3.fromDegreesArrayHeights(route),
            width: 5,
            // clampToGround: true, //贴地
            material: Cesium.Color.fromCssColorString(item.lineColor) || Cesium.Color.YELLOW,
            interval: 3000,
          },
        },
      }
      this.entities.set(`${config.name}_${item.name}_${index}`, cs.addPolyline(data))
    })
  }

  /**
   * water
   */
  createWater(config) {
    config = {
      id: 'water',
      positions: [
        [118.82007781481538, 32.26715119680075, 0],
        [118.8205387353803, 32.26714728339982, 0],
        [118.82063445311678, 32.26390316994469, 0],
        [118.82025373218201, 32.26389548340365, 0],
      ],
      maxHeight: 8,
      minHeight: 2,
    }
    if (!config.id) {
      return
    }

    const coordiantes = []
    for (const val of config.positions.values()) {
      coordiantes.push(val[0])
      coordiantes.push(val[1])
      coordiantes.push(config.maxHeight || val[2] || 0)
    }
    const positions = Cesium.Cartesian3.fromDegreesArrayHeights(coordiantes)

    const waterPrimitive = new Cesium.Primitive({
      show: true, // 默认隐藏
      allowPicking: true,
      geometryInstances: new Cesium.GeometryInstance({
        geometry: new Cesium.PolygonGeometry({
          polygonHierarchy: new Cesium.PolygonHierarchy(positions),
          extrudedHeight: config.minHeight || 0, //注释掉此属性可以只显示水面
          perPositionHeight: true,
        }),
      }),

      // 可以设置内置的水面shader
      appearance: new Cesium.EllipsoidSurfaceAppearance({
        material: new Cesium.Material({
          fabric: {
            type: 'Water',
            uniforms: {
              //baseWaterColor:new Cesium.Color(0.0, 0.0, 1.0, 0.5),
              //blendColor: new Cesium.Color(0.0, 0.0, 1.0, 0.5),
              //specularMap: 'gray.jpg',
              normalMap: require('../../../assets/images/common/waterNormals.jpg'),
              frequency: 1000.0,
              animationSpeed: 0.01,
              amplitude: 10.0,
            },
          },
        }),
        fragmentShaderSource:
          'varying vec3 v_positionMC;\nvarying vec3 v_positionEC;\nvarying vec2 v_st;\nvoid main()\n{\nczm_materialInput materialInput;\nvec3 normalEC = normalize(czm_normal3D * czm_geodeticSurfaceNormal(v_positionMC, vec3(0.0), vec3(1.0)));\n#ifdef FACE_FORWARD\nnormalEC = faceforward(normalEC, vec3(0.0, 0.0, 1.0), -normalEC);\n#endif\nmaterialInput.s = v_st.s;\nmaterialInput.st = v_st;\nmaterialInput.str = vec3(v_st, 0.0);\nmaterialInput.normalEC = normalEC;\nmaterialInput.tangentToEyeMatrix = czm_eastNorthUpToEyeCoordinates(v_positionMC, materialInput.normalEC);\nvec3 positionToEyeEC = -v_positionEC;\nmaterialInput.positionToEyeEC = positionToEyeEC;\nczm_material material = czm_getMaterial(materialInput);\n#ifdef FLAT\ngl_FragColor = vec4(material.diffuse + material.emission, material.alpha);\n#else\ngl_FragColor = czm_phong(normalize(positionToEyeEC), material);\
            gl_FragColor.a=0.5;\n#endif\n}\n', //重写shader，修改水面的透明度
      }),
    })

    this.primitives.set(config.id, waterPrimitive)
    this.viewer.scene.primitives.add(waterPrimitive)
  }

  /**
   * @description 实体左键点击事件
   * @param {Function} callBack 事件处理方法
   * @param {Boolean} noEntity 是否需要实体存在， 默认需要
   */
  entityLeftClickEvent(callBack, noEntity = false) {
    this.unBindEntityLeftClickEvent()
    const _this = this
    this._leftClickHandler = new Cesium.ScreenSpaceEventHandler(_this.viewer.scene.canvas)
    this._leftClickHandler.setInputAction(function (movement) {
      // 拾取坐标
      this.viewer.scene.pick(movement.position) // pick,这个得打开，否则获取不准。
      var pick = this.getCatesian3FromPX(movement.position)
      const height = Cesium.Cartographic.fromCartesian(pick).height
      const lat = Cesium.Math.toDegrees(Cesium.Cartographic.fromCartesian(pick).latitude)
      const lng = Cesium.Math.toDegrees(Cesium.Cartographic.fromCartesian(pick).longitude)

      console.log('点击位置经纬度及高度', [lng, lat, height >= 0 ? height : 0])

      // 不需要实体存在时仅返回点击处经纬度
      if (noEntity) {
        callBack([lng, lat, height >= 0 ? height : 0])
      } else {
        var pick = _this.viewer.scene.pick(movement.position)

        if (Cesium.defined(pick)) {
          callBack(pick.id || pick, [lng, lat, height])
        }
      }
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK)
  }

  /**
   * 移除实体左键点击事件
   */
  unBindEntityLeftClickEvent() {
    if (!this._leftClickHandler) {
      return
    }

    this._leftClickHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK) //移除事件
    this._leftClickHandler.destroy()
    this._leftClickHandler = null
  }

  /**
   * 添加3D地图渲染事件
   * @param {Function} callBack
   */
  mapPostRenderEvent(callBack) {
    this.unBindPostRenderEvent()
    this._postRenderEvent = callBack
    this.viewer.scene.postRender.addEventListener(callBack)
  }

  /**
   * 移除3D地图渲染事件
   */
  unBindPostRenderEvent() {
    if (!this._postRenderEvent) {
      return
    }
    this.viewer.scene.postRender.removeEventListener(this._postRenderEvent)
    this._postRenderEvent = null
  }
}

// 返回 此时 时间戳
function getTimeStamp() {
  return new Date().toUTCString()
}

export default CesiumMap3DControl
